﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;

namespace RoketPack
{
    public class Aero
    {
        [DllImport("user32.dll")]
        private static extern Int32 FlashWindowEx(ref FLASHWINFO pwfi);
        [DllImport("dwmapi.dll", PreserveSig = false)]
        private static extern void DwmExtendFrameIntoClientArea
                        (IntPtr hwnd, ref Margins margins);
        [DllImport("dwmapi.dll")]
        private extern static int DwmIsCompositionEnabled(ref int enabled);
        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern IntPtr CreateCompatibleDC(IntPtr hDC);
        [DllImport("gdi32.dll", ExactSpelling = true)]
        private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern bool DeleteObject(IntPtr hObject);
        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern bool DeleteDC(IntPtr hdc);
        [DllImport("gdi32.dll")]
        private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
        [DllImport("UxTheme.dll", CharSet = CharSet.Unicode)]
        private static extern int DrawThemeTextEx(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, string text, int iCharCount, int dwFlags, ref RECT pRect, ref DTTOPTS pOptions);
        [DllImport("uxtheme.dll")]
        private extern static int SetWindowThemeAttribute(
            IntPtr hWnd,
            WINDOWTHEMEATTRIBUTETYPE wtype,
            ref WTA_OPTIONS attributes,
            uint size);
        [DllImport("gdi32.dll")]
        private static extern IntPtr CreateDIBSection(IntPtr hdc, BITMAPINFO pbmi, uint iUsage, int ppvBits, IntPtr hSection, uint dwOffset);

        [StructLayout(LayoutKind.Sequential)]
        private struct FLASHWINFO
        {
            public UInt32 cbSize;
            public IntPtr hwnd;
            public Int32 dwFlags;
            public UInt32 uCount;
            public Int32 dwTimeout;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct WTA_OPTIONS
        {
            public WTNCA dwFlags;
            public WTNCA dwMask;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct DTTOPTS
        {
            public int dwSize;
            public int dwFlags;
            public int crText;
            public int crBorder;
            public int crShadow;
            public int iTextShadowType;
            public POINT ptShadowOffset;
            public int iBorderSize;
            public int iFontPropId;
            public int iColorPropId;
            public int iStateId;
            public bool fApplyOverlay;
            public int iGlowSize;
            public int pfnDrawTextCallback;
            public IntPtr lParam;
        }

        private const int DTT_COMPOSITED = 8192;
        private const int DTT_GLOWSIZE = 2048;
        private const int DTT_TEXTCOLOR = 1;

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public POINT(int x, int y)
            {
                this.x = x;
                this.y = y;
            }

            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class BITMAPINFO
        {
            public int biSize;
            public int biWidth;
            public int biHeight;
            public short biPlanes;
            public short biBitCount;
            public int biCompression;
            public int biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public int biClrUsed;
            public int biClrImportant;
            public byte bmiColors_rgbBlue;
            public byte bmiColors_rgbGreen;
            public byte bmiColors_rgbRed;
            public byte bmiColors_rgbReserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;

            public RECT(int left, int top, int right, int bottom)
            {
                Left = left;
                Top = top;
                Right = right;
                Bottom = bottom;
            }

            public RECT(Rectangle rectangle)
            {
                Left = rectangle.X;
                Top = rectangle.Y;
                Right = rectangle.Right;
                Bottom = rectangle.Bottom;
            }

            public Rectangle ToRectangle()
            {
                return new Rectangle(Left, Top, Right - Left, Bottom - Top);
            }

            public override string ToString()
            {
                return "Left: " + Left + ", " + "Top: " + Top + ", Right: " + Right + ", Bottom: " + Bottom;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct Margins
        {
            public int Left;
            public int Right;
            public int Top;
            public int Bottom;
        }

        [Flags]
        public enum WTNCA : uint
        {
            NODRAWCAPTION = 1,
            NODRAWICON = 2,
            NOSYSMENU = 4,
            NOMIRRORHELP = 8,
            VALIDBITS = NODRAWCAPTION | NODRAWICON | NOSYSMENU | NOMIRRORHELP
        }

        public enum TextStyle
        {
            Normal,
            Glowing
        }

        private enum WINDOWTHEMEATTRIBUTETYPE : uint
        {
            /// <summary>Non-client area window attributes will be set.</summary>
            WTA_NONCLIENT = 1,
        }

        internal static void SetMargins(Form form, int top, int left, int bottom, int right)
        {
            Margins margins = new Margins();
            margins.Top = top;
            margins.Left = left;
            margins.Bottom = bottom;
            margins.Right = right;
            Aero.DwmExtendFrameIntoClientArea(form.Handle, ref margins);
        }

        /// <summary>
        /// Whether or not the Aero effects are currently enabled on this system.
        /// </summary>
        internal static bool Enabled
        {
            get
            {
                try
                {
                    if (System.Environment.OSVersion.Version.Major < 6) return
                        false;

                    int enabled = 0;
                    DwmIsCompositionEnabled(ref enabled);

                    if (enabled == 0)
                        return false;
                }
                catch
                {
                    return false;
                }

                return true;
            }
        }

        internal static void SetWindowThemeAttributes(Form form, WTNCA attributes)
        {
            // This tests that the OS will support what we want to do. Will be false on Windows XP and earlier,
            // as well as on Vista and 7 with Aero Glass disabled.
            if (!Aero.Enabled)
                return;

            WTA_OPTIONS options = new WTA_OPTIONS();
            options.dwFlags = attributes;
            options.dwMask = WTNCA.VALIDBITS;

            // The SetWindowThemeAttribute API call takes care of everything
            SetWindowThemeAttribute(
                form.Handle,
                WINDOWTHEMEATTRIBUTETYPE.WTA_NONCLIENT,
                ref options,
                (uint)Marshal.SizeOf(typeof(WTA_OPTIONS)));
        }

        /// <summary>
        /// Brings attention to the specified form by flashing the task bar
        /// and window title until it is brought back into focus.
        /// </summary>
        /// <param name="form"></param>
        public static void Attention(Form form)
        {
            FLASHWINFO fw = new FLASHWINFO();

            const int FLASHW_STOP = 0; // stop flashing
            const int FLASHW_CAPTION = 1; // flash the window title
            const int FLASHW_TRAY = 2; // flash the taskbar button
            const int FLASHW_ALL = 3; // 1 | 2
            const int FLASHW_TIMER = 4; // flash continuously 
            const int FLASHW_TIMERNOFG = 12; // flash until the window comes to the foreground 

            fw.cbSize = Convert.ToUInt32(Marshal.SizeOf(typeof(FLASHWINFO)));
            fw.hwnd = form.Handle;
            fw.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG;
            fw.uCount = UInt32.MaxValue;

            FlashWindowEx(ref fw);
        }

        /// <summary>
        /// Converts an image into an icon.
        /// </summary>
        /// <param name="img">The image that shall become an icon</param>
        /// <param name="size">The width and height of the icon. Standard
        /// sizes are 16x16, 32x32, 48x48, 64x64.</param>
        /// <param name="keepAspectRatio">Whether the image should be squashed into a
        /// square or whether whitespace should be put around it.</param>
        /// <returns>The generated icon.</returns>
        /// <remarks>Sourced from http://www.dreamincode.net/code/snippet1684.htm. </remarks>
        internal static Icon MakeIcon(Image img, int size, bool keepAspectRatio)
        {
            Bitmap square = new Bitmap(size, size); // create new bitmap
            Graphics g = Graphics.FromImage(square); // allow drawing to it

            int x, y, w, h; // dimensions for new image

            if (!keepAspectRatio || img.Height == img.Width)
            {
                // just fill the square
                x = y = 0; // set x and y to 0
                w = h = size; // set width and height to size
            }
            else
            {
                // work out the aspect ratio
                float r = (float)img.Width / (float)img.Height;

                // set dimensions accordingly to fit inside size^2 square
                if (r > 1)
                { // w is bigger, so divide h by r
                    w = size;
                    h = (int)((float)size / r);
                    x = 0; y = (size - h) / 2; // center the image
                }
                else
                { // h is bigger, so multiply w by r
                    w = (int)((float)size * r);
                    h = size;
                    y = 0; x = (size - w) / 2; // center the image
                }
            }

            // make the image shrink nicely by using HighQualityBicubic mode
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            g.DrawImage(img, x, y, w, h); // draw image with specified dimensions
            g.Flush(); // make sure all drawing operations complete before we get the icon

            // following line would work directly on any image, but then
            // it wouldn't look as nice.
            return Icon.FromHandle(square.GetHicon());
        }

        internal static void DrawText(Graphics graphics, string text, Font font, Rectangle bounds, Color color, TextFormatFlags flags, TextStyle textStyle)
        {
            IntPtr primaryHdc = graphics.GetHdc();

            // Create a memory DC so we can work offscreen
            IntPtr memoryHdc = CreateCompatibleDC(primaryHdc);

            // Create a device-independent bitmap and select it into our DC
            BITMAPINFO info = new BITMAPINFO();
            info.biSize = Marshal.SizeOf(info);
            info.biWidth = bounds.Width + 16;
            info.biHeight = -(bounds.Height + 16);
            info.biPlanes = 1;
            info.biBitCount = 32;
            info.biCompression = 0; // BI_RGB
            IntPtr dib = CreateDIBSection(primaryHdc, info, 0, 0, IntPtr.Zero, 0);
            SelectObject(memoryHdc, dib);

            // Create and select font
            IntPtr fontHandle = font.ToHfont();
            SelectObject(memoryHdc, fontHandle);

            // Draw glowing text
            System.Windows.Forms.VisualStyles.VisualStyleRenderer renderer = new System.Windows.Forms.VisualStyles.VisualStyleRenderer(System.Windows.Forms.VisualStyles.VisualStyleElement.Window.Caption.Active);
            DTTOPTS dttOpts = new DTTOPTS();
            dttOpts.dwSize = Marshal.SizeOf(typeof(DTTOPTS));

            if (textStyle == TextStyle.Glowing)
            {
                dttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE | DTT_TEXTCOLOR;
            }
            else
            {
                dttOpts.dwFlags = DTT_COMPOSITED | DTT_TEXTCOLOR;
            }
            dttOpts.crText = ColorTranslator.ToWin32(color);
            dttOpts.iGlowSize = 8; // This is about the size Microsoft Word 2007 uses
            RECT textBounds = new RECT(8, 8, (bounds.Right - bounds.Left) + 16, (bounds.Bottom - bounds.Top) + 16);
            DrawThemeTextEx(renderer.Handle, memoryHdc, 0, 0, text, -1, (int)flags, ref textBounds, ref dttOpts);

            // Copy to foreground
            const int SRCCOPY = 0x00CC0020;
            BitBlt(primaryHdc, bounds.Left - 8, bounds.Top - 8, bounds.Width + 16, bounds.Height + 16, memoryHdc, 0, 0, SRCCOPY);
              
            // Clean up
            DeleteObject(fontHandle);
            DeleteObject(dib);
            DeleteDC(memoryHdc);

            graphics.ReleaseHdc(primaryHdc);
        }
    }
}
